package Methods;

import java.util.Arrays;
import function.Function;
import function.Parser;

public class Bisection implements Methods {
	// output constants
	private static final byte k = 0, a = 1, c = 2, b = 3, fc = 4;
	// input constants
	private static final byte func = 0, pointOne = 1, pointTwo = 2,
			tolerance1 = 3;

	double[][] result;
	Function function;
	double point1;
	double point2;
	double tolerance;

	
	public Bisection(String f,double p1,double p2) throws Exception{
		this.function = Parser.toFunction(f);
		this.point1 = p1;
		this.point2 = p2;
		this.tolerance = 0.01;
		
		this.result = new double[(int) (1 + Math.round((Math.log(point2
				- point1) - Math.log(tolerance))
				/ Math.log(2)))][5];
		// Initially fill array with -1
		for (double[] a : result)
			Arrays.fill(a, -1);
		
	}
	
	public Bisection(String input[]) throws Exception {
		this.function = Parser.toFunction(input[func]);
		this.point1 = Double.parseDouble(input[pointOne]);
		this.point2 = Double.parseDouble(input[pointTwo]);
		this.tolerance = Double.parseDouble(input[tolerance1]);

		this.result = new double[(int) (1 + Math.round((Math.log(point2
				- point1) - Math.log(tolerance))
				/ Math.log(2)))][5];
		// Initially fill array with -1
		for (double[] a : result)
			Arrays.fill(a, -1);
	}

	private void iterate(int index, double point1, double point2) {
		if(index >=result.length)return;
		result[index][k] = index;// k
		result[index][a] = point1;
		result[index][b] = point2;
		double c1 = (point1 + point2) / 2;
		result[index][c] = c1;
		result[index][fc] = function.evaluate(c1);

		if (Math.abs(point2 - point1) < tolerance) {
			// System.out.println(function.evaluate((point2+point1)/2));
			return;
		}

		if (function.evaluate(c1) == 0)
			iterate(index + 1, c1, c1);
		else if (function.evaluate(point1) * function.evaluate(c1) < 0)
			iterate(index + 1, point1, c1);
		else {
			iterate(index + 1, c1, point2);
		}
	}

	@Override
	public String[] getInputNames() {
		return new String[] { "Function", "Point a", "Point b", "Tolerance" };
	}

	@Override
	public String[] getOutputAddresses() {

		return new String[] { "k","a", "c", "b", "F(c)" };
	}

	@Override
	public String[] getSampleInput() {
		// TODO Auto-generated method stub
		return new String[] { "x*sin(x)-1", "0", "2", ".01" };
	}

	@Override
	public double[][] solve() {
		iterate(0, point1, point2);
		//trim
		int i = 0;
		for(i = 0 ;i<result.length;i++)
		{
			if(result[i][0]==-1)break;
		}
		double [][] trim = Arrays.copyOf(result, i);
		return trim;
	}

	@Override
	public double getError() {
		return 0;
	}

	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
/*		Bisection testBisection = new Bisection(new String[] { "x*sin(x)-1",
				"0", "2", ".01" });*/
		Bisection testBisection = new Bisection("x*sin(x)-1",
				0, 2);
		
		double[][] testResult = testBisection.solve();
		
		for (double[] a : testResult)
			System.out.println(Arrays.toString(a));

	}

}
